package com.esdk.sql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.esdk.esdk;
import com.esdk.sql.orm.*;
import com.esdk.utils.TimeMeter;

/**批量replace into（注意只有mysql才支持该功能，另外，是先删除再插入，而不是更新！有可能误清空其他字段，所以一定要把该条记录的所有字段的值都要写入）*/
public class BatchReplace implements ISQL, IShowSql{
  protected String tableName;
	protected Connection _conn;
  protected String replaceSQL;
	protected boolean isShowSql=true;
	protected List<IRow> rowList=new ArrayList<IRow>();
	protected int performCount=0;
	protected boolean ignoreRowChanged=false;
	private SqlDialect sqlDialect=SQLAssistant.defaultDialect;
	private int retryTimes=0, retryIntervalMs=1000;
	private long executeConcumingTime;

	/**默认ignoreRowChanged=false，即需要判断row对象是否被修改过*/
  public BatchReplace(String tablename,Connection conn){
    this.tableName=tablename;
    setConnection(conn);
  }
	public BatchReplace(String tablename,Connection conn,boolean isForceReplace){
		this.tableName=tablename;
		setConnection(conn);
		this.ignoreRowChanged=isForceReplace;
	}

	@Override
  public String getSQL(){
  	parse();
    return replaceSQL;
  }

	public String getTableName(){
		return tableName;
	}

	@Override
  public void clear(){
    tableName=null;
    rowList.clear();
  }
  
  public void clearValues() {
  	 rowList.clear();
  }
  
  public int size() {
  	return  rowList.size();
  }

	@Override public BatchReplace showSql(boolean isShowSql){
		this.isShowSql=isShowSql;
		return this;
	}

	@Override public boolean showSql(){
		return this.isShowSql;
	}

	public BatchReplace addValues(IRow row) {
		if(ignoreRowChanged || row.isChanged()) {
			rowList.add(row);
		}
		return this;
	}

  public BatchReplace addValues(ParentRow row) {
		if(ignoreRowChanged || row.isChanged()) {
	  	rowList.add(row);
  	}
  	return this;
  }

	public <PR extends ParentRow> BatchReplace addValues(Collection<PR> collection){
		for(ParentRow row:collection) {
			addValues(row);
		}
		return this;
	}
	public <R extends IRow> BatchReplace addValues(IRowSet<R> rs){
		for(IRow row:rs) {
			addValues(row);
		}
		return this;
	}
	public List<IRow> getValues(){
		return rowList;
	}
  
  String getEllipseSql() {
  	StringBuilder result=new StringBuilder();
  	if(!rowList.isEmpty()) {
  		result.append(sqlDialect.parseBatchReplace(tableName,rowList.subList(0,1),ignoreRowChanged));
  	}
		result.append("...");
  	return result.toString();
  }

	@Override
  public void parse(){
  	StringBuilder sb=sqlDialect.parseBatchReplace(tableName,rowList,ignoreRowChanged);
    replaceSQL=sb.toString();
  }

	/**自动分段执行*/
	public int perform(int limit,boolean autoCleanValues) throws SQLException{
		List<IRow> all=rowList;
		int count=size();
		for(int i=0,n=esdk.math.ceil(count,limit);i<n;i++){
			rowList=all.subList(i*limit,esdk.math.min((i+1)*limit,all.size()));
			perform(false);
		}
		if(autoCleanValues)
			clearValues();
		else
			rowList=all;
		performCount=count;
		return performCount;
	}

  /**如果达到或超出的数量则执行*/
	public int performIfOverload(int limit) throws SQLException{
		if(size()>=limit) {
			int before=performCount;
			int count=perform(true);
			performCount=before+count;
			return count;
		}
		else
			return 0;
	}
  
  /**执行批处理sql并清空values值*/
	public int perform(boolean autoCleanValues) throws SQLException{
		performCount=_perform();
		if(performCount==size()&&autoCleanValues)
			clearValues();
		return performCount;
	}

	@Override
	public int perform() throws SQLException{
		performCount=perform(true);
		return performCount;
	}

	/**获取影响的记录数*/
	public int getPerformCount(){
		return performCount;
	}

  private int _perform() throws SQLException{
    if(_conn==null)
      throw new SQLException("Connection is null,please invoke setConnectcion before perform");
    if(size()==0)
    	return 0;
		parse();
		try(Statement st=_conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY)){
			TimeMeter tm=TimeMeter.startNew();
			boolean flag=SQLAssistant.isPostgreSQL()?st.execute(replaceSQL):st.execute(replaceSQL,Statement.RETURN_GENERATED_KEYS);
			this.executeConcumingTime=tm.getElapse();
			if(isShowSql)
				SQLAssistant.showSql(this);
			return st.getUpdateCount();
		}catch(SQLException e){
			if(retryTimes>0 && e.getMessage().contains("Deadlock found")){
				esdk.error("可重试{}次批量插入SQL执行异常，错误提示：{}。执行的SQL：{}",retryTimes,e.toString(),this.getEllipseSql());
				retryTimes--;
				esdk.tool.sleep(this.retryIntervalMs);
				return _perform();
			}
			throw new SQLRuntimeException("批量插入SQL执行出现异常，错误提示："+e.toString()+"。执行的SQL为："+this.getEllipseSql(),e);
		}
  }

	public BatchReplace retry(int retryTimes){
		this.retryTimes=retryTimes;
		return this;
	}

	public BatchReplace retry(int retryTimes,int retryIntervalMs){
		this.retryTimes=retryTimes;
		this.retryIntervalMs=retryIntervalMs;
		return this;
	}

	@Override
  public Connection getConnection(){
    return _conn;
  }

	@Override
  public void setConnection(Connection conn){
    this._conn=conn;
		this.sqlDialect=SQLAssistant.setDefaultDialect(this._conn);
  }

	@Override public long getExecuteConsuming(){
		return executeConcumingTime;
	}

}
